package com.mxx.common.websecurity;

import com.mxx.common.exception.CommentException;
import com.mxx.common.utils.CollectionUtil;
import com.mxx.common.utils.Strings;
import com.mxx.tomcat.ResponseWriter;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.ForwardAuthenticationFailureHandler;
import org.springframework.security.web.authentication.ForwardAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.ForwardLogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;

/**
 * @Auther: Mr. Zeng
 * @Date: 2020/7/21 13:19
 * @Description: 暂无描述
 */

public class AuthenticationParameterBuilder {
    private AuthenticationParameter parameter;

    private AuthenticationParameterBuilder(AuthenticationParameter parameter) {
        this.parameter = parameter;
        this.parameter.setResourcePath(new ArrayList<>());
    }

    public static AuthenticationParameterBuilder builder() {
        return new AuthenticationParameterBuilder(new AuthenticationParameter());
    }

    public AuthenticationParameterBuilder verification(String verificationName, String storageName) {
        this.parameter.setVerificationName(verificationName).setStorageName(storageName);
        return this;
    }

    public AuthenticationParameterBuilder resource(String... path) {
        this.parameter.getResourcePath().addAll(CollectionUtil.toList(path));
        return this;
    }

    public AuthenticationParameterBuilder resource(String resource) {
        if (this.parameter == null) {
            this.parameter.getResourcePath().add(resource);
        }
        this.parameter.getResourcePath().add(resource);
        return this;
    }


    public AuthenticationParameterBuilder login(String loginPage, AuthenticationFailureHandler authenticationFailureHandler, AuthenticationSuccessHandler authenticationSuccessHandler) {
        this.parameter.setLoginPage(loginPage)
                .setFailureHandler(authenticationFailureHandler)
                .setSuccessHandler(authenticationSuccessHandler);
        return this;
    }

    public AuthenticationParameterBuilder login(String loginPage, String loginSuccessUrl, String loginFailureUrl) {
        this.parameter.setLoginPage(loginPage)
                .setFailureHandler(new ForwardAuthenticationFailureHandler(loginFailureUrl))
                .setSuccessHandler(new ForwardAuthenticationSuccessHandler(loginSuccessUrl));
        return this;
    }

    public AuthenticationParameterBuilder loadLoginHandler(String loginPage, ResponseWriter responseWriter) {
        if (this.parameter.getAuthenticationBodyHandler() == null) {
            throw new CommentException(" AuthenticationBodyHandler is null");
        }
        if (responseWriter == null) {
            throw new CommentException(" ResponseWriter is null");
        }
        this.parameter.setLoginPage(loginPage)
                .setFailureHandler(new AuthenticationResponseBodyFailureHandler(responseWriter, this.parameter.getAuthenticationBodyHandler()))
                .setSuccessHandler(new AuthenticationResponseBodySuccessfulHandler(responseWriter, this.parameter.getAuthenticationBodyHandler()));
        return this;
    }

    public AuthenticationParameterBuilder detailsSource(AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource, AuthenticationProvider authenticationProvider) {
        this.parameter.setAuthenticationDetailsSource(authenticationDetailsSource).setAuthenticationProvider(authenticationProvider);
        return this;
    }

    public AuthenticationParameterBuilder loadValidateCodeSource() {
        if (Strings.isNotEmpty(this.parameter.getVerificationName(), this.parameter.getStorageName(), this.parameter.getAuthenticationBodyHandler())) {
            throw new CommentException(" The required parameter does not exist ");
        }
        detailsSource(new AuthenticationValidateCodeDetailsSource(this.parameter),
                new AuthenticationValidateCodeProvider(this.parameter.getAuthenticationBodyHandler(), this.parameter));
        return this;
    }

    public AuthenticationParameterBuilder logout(String logoutUrl, String logoutSuccessUrl) {
        this.parameter.setLogoutUrl(logoutUrl)
                .setLogoutSuccessHandler(new ForwardLogoutSuccessHandler(logoutSuccessUrl));
        return this;
    }

    public AuthenticationParameterBuilder logout(String logoutUrl, LogoutSuccessHandler logoutSuccessHandler) {
        this.parameter.setLogoutUrl(logoutUrl)
                .setLogoutSuccessHandler(logoutSuccessHandler);
        return this;
    }

    public AuthenticationParameterBuilder loginHandler(AuthenticationBodyHandler bodyHandler, PasswordEncoder passwordEncoder) {
        this.parameter.setAuthenticationBodyHandler(bodyHandler).setPasswordEncoder(passwordEncoder);
        return this;
    }

    public AuthenticationParameterBuilder sessionFailureHandler(AuthenticationFailureHandler sessionFailureHandler, SessionInformationExpiredStrategy sessionInformationExpiredStrategy) {
        this.parameter.setSessionFailureHandler(sessionFailureHandler).setSessionInformationExpiredStrategy(sessionInformationExpiredStrategy);
        return this;
    }

    public AuthenticationParameterBuilder loadSessionFailureHandler(ResponseWriter responseWriter) {
        if (this.parameter.getAuthenticationBodyHandler() == null) {
            throw new CommentException(" AuthenticationBodyHandler is null");
        }
        if (responseWriter == null) {
            throw new CommentException(" ResponseWriter is null");
        }
        this.parameter.setSessionFailureHandler(new AuthenticationResponseBodySessionFailureHandler(responseWriter, this.parameter.getAuthenticationBodyHandler()))
                .setSessionInformationExpiredStrategy(new AuthenticationResponseBodySessionExpiredHandler(responseWriter, this.parameter.getAuthenticationBodyHandler()));
        return this;
    }

    public AuthenticationParameterBuilder csrf() {
        this.parameter.setCsrf(true);
        return this;
    }

    public AuthenticationParameterBuilder sameOrigin() {
        this.parameter.setSameOrigin(true);
        return this;
    }

    public AuthenticationParameterBuilder unCsrf() {
        this.parameter.setCsrf(false);
        return this;
    }

    public AuthenticationParameterBuilder unSameOrigin() {
        this.parameter.setSameOrigin(false);
        return this;
    }


    public AuthenticationParameter build() {
        if (Strings.isNotEmpty(this.parameter.getVerificationName(), this.parameter.getStorageName(), this.parameter.getAuthenticationBodyHandler(),
                this.parameter.getSuccessHandler(), this.parameter.getFailureHandler())) {
            throw new CommentException(" The required parameter does not exist ");
        }
        return this.parameter;
    }
}
